<template>
    <div class="tnxbsv-cascader" :title="displayValue">
        <BInputGroup @click="toggleDropdown" :class="{'active': visible}">
            <BFormInput class="display"
                v-model="inputValue"
                :readonly="!filterable"
                :placeholder="placeholder"
                :disabled="disabled"
                @input="handleFilter"
                @focus="handleFocus"
            />
            <template #append>
                <BButton variant="light" class="btn-append">
                    <i v-if="clearable && !disabled && modelValue"
                        class="bi bi-x-circle me-1"
                        @click.stop="clearValue">
                    </i>
                    <i class="bi bi-chevron-down" :class="{'rotate': visible}"></i>
                </BButton>
            </template>
        </BInputGroup>

        <div v-show="visible" class="tnxbsv-cascader-dropdown">
            <div v-if="filtering" class="tnxbsv-cascader-search-list">
                <div v-for="(option, index) of filterResults"
                    :key="index"
                    class="tnxbsv-cascader-node"
                    @click="handleFilterOptionClick(option)">
                    <span>{{ getOptionPath(option) }}</span>
                </div>
            </div>
            <div v-else class="tnxbsv-cascader-menus">
                <BListGroup v-for="(options, level) of optionsArray"
                    :key="level"
                    class="tnxbsv-cascader-menu">
                    <BListGroupItem v-for="option of options"
                        :key="option[props.value]"
                        button
                        :disabled="option[props.disabled]"
                        :active="isOptionSelected(level, option)"
                        class="tnxbsv-cascader-node"
                        @click="handleOptionClick(level, option)">
                        <span>{{ option[props.label] }}</span>
                        <i v-if="option[props.children]" class="bi bi-chevron-right"></i>
                    </BListGroupItem>
                </BListGroup>
            </div>
        </div>
    </div>
</template>

<script>
import {BInputGroup, BFormInput, BButton, BListGroup, BListGroupItem,} from 'bootstrap-vue-next';

export default {
    name: 'TnxbsvCascader',
    components: {BInputGroup, BFormInput, BButton, BListGroup, BListGroupItem,},
    props: {
        modelValue: {
            type: String,
            default: '',
        },
        options: {
            type: Array,
            default: () => [],
        },
        placeholder: {
            type: String,
            default: '请选择',
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        parentSelectable: {
            type: Boolean,
            default: false,
        },
        separator: {
            type: String,
            default: ' / ',
        },
        clearable: {
            type: Boolean,
            default: false,
        },
        showAllLevels: {
            type: Boolean,
            default: true,
        },
        filterable: {
            type: Boolean,
            default: false,
        },
        filterMethod: {
            type: Function,
            default: (node, keyword) => node.label.toLowerCase().includes(keyword.toLowerCase()),
        },
        props: {
            type: Object,
            default: () => ({
                label: 'label',
                value: 'value',
                children: 'children',
                disabled: 'disabled'
            })
        },
        optionVisibleSize: {
            type: Number,
            default: 5,
        },
    },
    data() {
        return {
            visible: false,
            selections: this.initSelections(),
            optionsArray: [this.options],
            inputValue: '',
            filtering: false,
            filterResults: [],
        };
    },
    computed: {
        displayValue() {
            if (!this.selections.length) {
                return '';
            }
            // 如果不允许父节点选择，且最后一个选择是非叶子节点，则不显示任何值
            const lastSelection = this.selections[this.selections.length - 1];
            if (!this.parentSelectable && lastSelection[this.props.children]?.length > 0) {
                return this.inputValue;
            }
            const labels = this.selections.filter(item => item).map(item => item[this.props.label]);
            return this.showAllLevels ? labels.join(this.separator) : labels[labels.length - 1];
        },
    },
    watch: {
        displayValue() {
            if (!this.filtering) {
                this.inputValue = this.displayValue;
            }
        },
        modelValue(newValue) {
            const currentValue = this.selections.length > 0 ? this.selections[this.selections.length - 1][this.props.value] : '';
            if (newValue !== currentValue) {
                this.selections = this.initSelections();
            }
        },
        options: {
            deep: true,
            handler(newOptions) {
                this.optionsArray = [newOptions];
                // 清空已选项，避免选项更新后出现无效选择
                this.selections = [];
                this.inputValue = '';
                this.$emit('update:modelValue', '');
            },
        },
        optionVisibleSize: {
            immediate: true,
            handler(value) {
                const fullHeight = value * 2.5 + 1.25;
                document.documentElement.style.setProperty('--tnxbsv-cascader-menu-max-height', `${fullHeight}rem`);
            }
        }
    },
    mounted() {
        document.addEventListener('click', this.handleClickOutside);
    },
    beforeUnmount() {
        document.removeEventListener('click', this.handleClickOutside);
    },
    methods: {
        initSelections() {
            if (!this.modelValue || !this.options) {
                return [];
            }
            const path = this.findPathByValue(this.options, this.modelValue);
            if (path) {
                // 初始化 optionsArray
                path.forEach((option, index) => {
                    if (index < path.length - 1) {
                        const children = option[this.props.children];
                        if (children) {
                            this.optionsArray[index + 1] = children;
                        }
                    }
                });
                return path;
            }
            return [];
        },
        findPathByValue(options, value, path = []) {
            for (const option of options) {
                const currentPath = [...path, option];
                if (option[this.props.value] === value) {
                    return currentPath;
                }
                if (option[this.props.children]) {
                    const foundPath = this.findPathByValue(option[this.props.children], value, currentPath);
                    if (foundPath) {
                        return foundPath;
                    }
                }
            }
            return null;
        },
        clearValue(e) {
            e.stopPropagation();
            this.selections = [];
            this.optionsArray = [this.options];
            this.$emit('update:modelValue', '');
            this.inputValue = '';
        },
        handleFilter(value) {
            if (!this.filterable) {
                return;
            }
            this.filtering = !!value;
            if (!value) {
                this.filterResults = [];
                this.inputValue = this.displayValue;
                return;
            }
            this.filterResults = this.searchOptions(this.options, value);
        },
        handleFocus() {
            if (this.filterable && !this.filtering) {
                this.inputValue = '';
            }
        },
        searchOptions(options, keyword, path = []) {
            let results = [];
            options.forEach(option => {
                if (this.filterMethod(option, keyword)) {
                    results.push({
                        ...option,
                        path: [...path, option],
                    });
                }
                const children = option[this.props.children];
                if (children) {
                    results = results.concat(
                        this.searchOptions(children, keyword, [...path, option])
                    );
                }
            });
            return results;
        },
        getOptionPath(option) {
            return option.path.map(item => item[this.props.label]).join(this.separator);
        },
        handleFilterOptionClick(option) {
            this.selections = option.path;
            this.visible = false;
            this.filtering = false;
            this.inputValue = this.displayValue;
            this.$emit('update:modelValue', option[this.props.value]);
        },
        toggleDropdown() {
            if (!this.disabled) {
                this.visible = !this.visible;
                if (this.visible) {
                    this.$nextTick(() => {
                        const dropdown = this.$el.querySelector('.tnxbsv-cascader-dropdown');
                        const input = this.$el.querySelector('.input-group');
                        if (dropdown && input) {
                            const rect = input.getBoundingClientRect();
                            dropdown.style.top = `${rect.bottom}px`;
                            dropdown.style.left = `${rect.left}px`;
                            // 移除固定宽度设置，让下拉菜单自动扩展
                            dropdown.style.zIndex = window.tnx.util.dom.minTopZIndex();
                        }
                    });
                }
            }
        },
        isOptionSelected(level, option) {
            return this.selections[level] &&
                this.selections[level][this.props.value] === option[this.props.value];
        },
        handleOptionClick(level, option) {
            if (option[this.props.disabled]) {
                return;
            }

            this.selections.splice(level);
            this.optionsArray.splice(level + 1);
            this.selections[level] = option;

            const children = option[this.props.children];
            if (children && children.length) {
                this.optionsArray.push(children);
            } else {
                this.visible = false;
                this.$emit('update:modelValue', option[this.props.value]);
            }
        },
        handleClickOutside(e) {
            if (!this.$el.contains(e.target)) {
                this.visible = false;
                if (this.selections.length > 0) {
                    const lastSelection = this.selections[this.selections.length - 1];
                    const hasChildren = lastSelection[this.props.children]?.length > 0;

                    if (!hasChildren || this.parentSelectable) {
                        // 如果是叶子节点，或者允许父节点选择，则发送最后选择的值
                        this.$emit('update:modelValue', lastSelection[this.props.value]);
                    }
                    // 不做任何清空操作，保持当前选择状态
                }
            }
        }
    },
};
</script>

<style>
.tnxbsv-cascader {
    position: relative;
    display: inline-block;
    width: 100%;
}

.tnxbsv-cascader .input-group.active .display,
.tnxbsv-cascader .input-group.active .btn-append {
    border-color: var(--bs-primary);
}

.tnxbsv-cascader .display {
    border-right: none;
    box-shadow: none;
    text-overflow: ellipsis;
    color: var(--bs-secondary-color);
    padding-right: 0.5rem;
}

.tnxbsv-cascader .display:focus {
    box-shadow: none;
}

.tnxbsv-cascader .btn-append,
.tnxbsv-cascader .btn-append:hover,
.tnxbsv-cascader .btn-append:active {
    padding-left: 0.5rem;
    border-color: var(--bs-border-color);
    border-left: none;
    background-color: transparent;
    color: var(--bs-tertiary-color);
}

.tnxbsv-cascader .btn-append .bi-chevron-down {
    display: inline-block;
    transition: transform 0.3s ease;
}

.tnxbsv-cascader .btn-append .rotate {
    transform: rotate(180deg);
}

.tnxbsv-cascader-dropdown {
    position: fixed;
    margin-top: 0.25rem;
    background: var(--bs-body-bg);
    border: var(--bs-border-width) solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    box-shadow: var(--bs-dropdown-box-shadow);
}

.tnxbsv-cascader-menus {
    display: flex;
    min-width: 11.25rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.tnxbsv-cascader-menus::-webkit-scrollbar {
    display: none;
}

.tnxbsv-cascader-menu {
    min-width: 11.25rem;
    border-right: var(--bs-border-width) solid var(--bs-border-color);
    flex-shrink: 0;
    border-radius: 0;
    max-height: var(--tnxbsv-cascader-menu-max-height);
    overflow-y: auto;
}

.tnxbsv-cascader-menu:first-child {
    border-top-left-radius: var(--bs-border-radius);
    border-bottom-left-radius: var(--bs-border-radius);
}

.tnxbsv-cascader-menu:last-child {
    border-right: none;
}

.tnxbsv-cascader-menu::-webkit-scrollbar {
    width: 6px;
}

.tnxbsv-cascader-menu::-webkit-scrollbar-track {
    background: transparent;
    border: none;
}

.tnxbsv-cascader-menu::-webkit-scrollbar-thumb {
    background-color: rgba(var(--bs-secondary-rgb), 0.3);
    border-radius: 3px;
    border: 1px solid transparent;
    background-clip: padding-box;
}

.tnxbsv-cascader-menu::-webkit-scrollbar-thumb:hover {
    background-color: rgba(var(--bs-secondary-rgb), 0.5);
}

.tnxbsv-cascader-search-list {
    min-width: 11.25rem;
    max-height: var(--tnxbsv-cascader-menu-max-height);
    overflow-y: auto;
    border-radius: var(--bs-border-radius);
}

.tnxbsv-cascader-search-list .tnxbsv-cascader-node {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tnxbsv-cascader-search-list::-webkit-scrollbar {
    width: 6px;
}

.tnxbsv-cascader-search-list::-webkit-scrollbar-track {
    background: transparent;
    border: none;
}

.tnxbsv-cascader-search-list::-webkit-scrollbar-thumb {
    background-color: rgba(var(--bs-secondary-rgb), 0.3);
    border-radius: 3px;
    border: 1px solid transparent;
    background-clip: padding-box;
}

.tnxbsv-cascader-search-list::-webkit-scrollbar-thumb:hover {
    background-color: rgba(var(--bs-secondary-rgb), 0.5);
}

.tnxbsv-cascader-node {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border: none;
    border-radius: 0;
}

.tnxbsv-cascader-node:hover {
    background-color: var(--bs-tertiary-bg);
}

.tnxbsv-cascader-node.active {
    color: var(--bs-primary);
    background-color: var(--bs-tertiary-bg);
}

.tnxbsv-cascader-node.is-disabled {
    color: var(--bs-secondary-color);
    cursor: not-allowed;
}

/* 使用固定的 Bootstrap 断点值 */
@media (max-width: 576px) {
    .tnxbsv-cascader-menu {
        min-width: 8.75rem;
        max-height: var(--tnxbsv-cascader-menu-max-height);
    }

    .tnxbsv-cascader-node {
        padding: var(--bs-dropdown-item-padding-y) 0.5rem;
    }

    .tnxbsv-cascader-search-list {
        min-width: 8.75rem;
    }
}
</style>
